# @Data  : 20200814
# @Author: james
# @Description  : 统一的异常处理类，封装系统异常，包括selenium等，在装饰器顺序里，应该换在最外层
import time
import traceback

from decorator import decorator
from robot.api import logger
from selenium.common.exceptions import ElementNotInteractableException, ElementClickInterceptedException

from Lib.zcm.assistant import FwVariables
from Lib.zcm.common.ui.PageElement import PageElement
from Lib.zcm.exception.TestException import TestException
from Lib.zcm.exception.V9ErrorPage import V9ErrorPage
from Lib.zcm.exception.V90EErrorPage import V90EErrorPage
from Lib.zcm.portal.VersionEnum import VersionEnum


@decorator
def exception_handler(func, ignores=(), *args, **kwargs):
    result = None

    logger.debug("before exception handler args={0} and kwargs={1}".format(args, kwargs))
    # 最多尝试3次
    try_count = 0
    while 0 <= try_count <= 3:
        try_count += 1
        try:
            result = func(*args, **kwargs)
        except Exception as e:
            if not is_ignore_exception(e, ignores):
                traceback.print_exc()
                if not is_re_try_exception(e) or try_count > 3:
                    try_count = -1
                    raise TestException.by_exception(e)
                else:
                    logger.warn(f"Try {try_count} times...")
                    time.sleep(1)
            else:
                try_count = -1  # 如果是忽略的错误，默认成功
        finally:
            try_count = -1  # 执行没有报错
            # 对于V9类型的测试项目，查看有没有错误提示页面
            if FwVariables.get_version() == VersionEnum.V9.version and len(args) > 1 and isinstance(args[1], PageElement):
                error_page = V9ErrorPage(args[1].get_webdriver())
                if error_page.is_error_page_exist():
                    raise error_page.get_exception()
            if FwVariables.get_version() == VersionEnum.V90E.version and len(args) > 1 and isinstance(args[1], PageElement):
                error_page = V90EErrorPage(args[1].get_webdriver())
                if error_page.is_error_page_exist():
                    raise error_page.get_exception()
    return result


def is_ignore_exception(exception: Exception, ignores):
    for ignore_exception in ignores:
        if isinstance(exception, ignore_exception):
            logger.warn('A {0} exception occurred but ignore.\nException: {1}'.format(ignore_exception, exception))
            return True
    return False


def is_re_try_exception(exception: Exception):
    return isinstance(exception, (ElementNotInteractableException, ElementClickInterceptedException))

